package service;

import common.Message;
import common.MessageType;
import common.User;

import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.ArrayList;
import java.util.concurrent.ConcurrentHashMap;


/**
 * @Description: 服务端监听9999端口，等待客户连接并保持通信
 * @CreateTime: 2024-02-26  14:27
 * @Author: JAMILA
 * @Version: 1.0
 */

public class IMServer {
    private ServerSocket serverSocket = null;
    /**
     * 创建集合，保存用户信息；模拟数据库(可以使用ConcurrentHashMap-可以处理并发的结合，没有线程安全问题)
     * HashMap没有处理线程安全，因此在多线程下是不安全的
     * ConcurrentHashMap处理了线程安全，即线程同步处理，在多线程情况下是安全的
     */
    private static ConcurrentHashMap<String,User> validUsers=new ConcurrentHashMap<>();

    /**
     * 创建集合，保存离线消息
     */
    private static ConcurrentHashMap<String, ArrayList<Message>> offlineDb=new ConcurrentHashMap<>();

    static {
        //静态代码块中初始化validUsers
        validUsers.put("100",new User("100","123456"));
        validUsers.put("200",new User("200","123456"));
        validUsers.put("300",new User("300","123456"));
        validUsers.put("至尊宝",new User("至尊宝","123456"));
        validUsers.put("紫霞仙子",new User("紫霞仙子","123456"));
        validUsers.put("JAMILA",new User("JAMILA","123456"));
    }

    //验证用户账号密码
    private boolean checkUser(String useId,String pwd){
        boolean res=false;
        if (validUsers.containsKey(useId)){
            User user = validUsers.get(useId);
            if (useId.equals(user.getUserId())&&pwd.equals(user.getPasswd())){
                res=true;
            }
        }
        return res;
    }

    public IMServer() {
        //端口可以卸载配置文件中
        try {
            System.out.println("服务端在9999端口监听....");
            serverSocket = new ServerSocket(9999);

            //服务器推送消息
            new Thread(new SendNewsToAllService()).start();

            //服务端一直保持监听
            while (true) {
                Socket socket = serverSocket.accept();//如果没有客户端连接，就会阻塞在此处
                ObjectInputStream ois = new ObjectInputStream(socket.getInputStream());
                ObjectOutputStream oos = new ObjectOutputStream(socket.getOutputStream());
                User user = (User) ois.readObject();
                Message message = new Message();
                if (checkUser(user.getUserId(), user.getPasswd())) {
                    //用户账号密码验证通过
                    message.setMesType(MessageType.MESSAGE_LOGIN_SUCCEED);
                    //将message回复给客户端
                    oos.writeObject(message);
                    //创建线程，和客户端保持通信，该线程需要持有socket对象
                    ServerConnectClientThread serverConnectClientThread = new ServerConnectClientThread(socket, user.getUserId());
                    //启动线程
                    serverConnectClientThread.start();
                    //将该线程对象放入到集合中进行管理
                    ManageClientThreads.addServerConnectClientThread(user.getUserId(), serverConnectClientThread);

                    //用户登录成功，获取离线信息
                    ArrayList<Message> messages = offlineDb.get(user.getUserId());
                    if (messages!=null&&messages.size()>0){
                        //将离线数据库中所有离线信息发送给对应用户
                        for (Message m : messages) {
                            new ObjectOutputStream(socket.getOutputStream()).writeObject(m);
                        }
                        //删除离线数据库中该用户的数据
                        offlineDb.remove(user.getUserId());
                    }

                } else {
                    //用户账号密码验证不通过
                    message.setMesType(MessageType.MESSAGE_LOGIN_FAIL);
                    //将message回复给客户端
                    oos.writeObject(message);
                    socket.close();
                }

            }


        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            //若果服务端退出了while循环，说明服务端不再监听9999，需要关闭serverSocket
            try {
                serverSocket.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }

    //增加留言消息
    public static void addOfflineMessage(String getterId,Message message){
        //判断离线数据库中是否有该接收者的信息，若没有则新添加，否则将新消息追加进去
        if (offlineDb.containsKey(getterId)){//追加
            ArrayList<Message> messages = offlineDb.get(getterId);
            messages.add(message);
        }else {//新增
            ArrayList<Message> messages =new ArrayList<>();
            messages.add(message);
            offlineDb.put(getterId,messages);
        }
    }


}
